package com.stockz.downloader.breadth.bse;

import java.io.IOException;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.zip.ZipInputStream;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import com.stockz.downloader.DownloaderCallback;
import com.stockz.downloader.breadth.BarListBreadthDownload;
import com.stockz.downloader.breadth.BarListBreadthDownloader;
import com.stockz.downloader.breadth.BarListBreadthDownloaderCallback;
import com.stockz.downloader.util.DownloaderConstant;
import com.stockz.downloader.util.DownloaderUtil;
import com.stockz.equity.model.Bar;
import com.stockz.equity.util.CoreConstant;

@Component
public class BseBarListBredthDownloader
<T extends BarListBreadthDownload<V,U>,
V extends List<U>, 
W extends BarListBreadthDownloaderCallback<T,V,W,U>, 
U extends Bar>  
implements BarListBreadthDownloader<T,V,W,U>{

	private static final Logger logger = Logger.getLogger(BseBarListBredthDownloader.class);
	
	@Autowired
	@Qualifier("download.url.bse.start")
	private String downloadUrlBseStart = "http://www.bseindia.com/bhavcopy/eq";
	@Autowired
	@Qualifier("download.url.bse.end")
	private String downloadUrlBseEnd = "_csv.zip";
	@Autowired
	@Qualifier("download.url.bse.date.format")
	private String downloadUrlBseDateFormat = "ddMMyy";
	
	public String constructDownloadUri(Date date) {
		logger.trace("Constructing download uri for BSE Bredth download");
		logger.trace("Using downloadUrlBseStart : "+downloadUrlBseStart);
		logger.trace("Using downloadUrlBseEnd : "+downloadUrlBseEnd);
		logger.trace("Using downloadUrlBseDateFormat : "+downloadUrlBseDateFormat);
		DateFormat dateFormat = new SimpleDateFormat(downloadUrlBseDateFormat);
		String uri = downloadUrlBseStart + dateFormat.format(date) + downloadUrlBseEnd;
		logger.trace("Uri for date "+date+" is "+uri);
		return 	uri;
	}
	
	public String getRawData(String uri) throws MalformedURLException, IOException{
		ZipInputStream inStream = new ZipInputStream(new URL(uri).openStream());
        byte[] buffer = new byte[1024];
        int nrBytesRead;
        StringWriter w = new StringWriter();
        if (inStream.getNextEntry() != null) {
            while ((nrBytesRead = inStream.read(buffer)) > 0) {
            	w.write(new String(buffer,0,nrBytesRead));
            }
        }
        w.flush();
        w.close();
        inStream.close();
		return w.toString();
	}
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public void download(T barListBreadthDownload, List<W> downloaderCallbackList)
			throws Exception {
        
		if(barListBreadthDownload == null  
				|| barListBreadthDownload.getStartDate() == null){
			logger.trace("Returning as either barListBreadthDownload or start date is null");
			return;
		}

		Date today = CoreConstant.dateFormat.parse(CoreConstant.dateFormat.format(new Date()));
		Date endDate = barListBreadthDownload.getEndDate();
		if(endDate == null  
				|| endDate.equals(today) 
				|| endDate.after(today)){
			endDate = today;
			List<Bar> barList = null;
			Calendar calendar = Calendar.getInstance();
			calendar.setTime(barListBreadthDownload.getStartDate());
			while(!calendar.getTime().after(endDate)){
				try{
					calendar.add(Calendar.DATE, 1);
					
					if(Calendar.SUNDAY == calendar.get(Calendar.DAY_OF_WEEK) 
							|| Calendar.SATURDAY == calendar.get(Calendar.DAY_OF_WEEK)){
						continue;
					}
					
					barListBreadthDownload.getBarList().clear();
			        barListBreadthDownload.setDownloadDate(calendar.getTime());
			        DownloaderUtil.executeCallbacks(barListBreadthDownload,
			        		DownloaderConstant.ACTION.ALL,
			        		DownloaderConstant.EVENT.START,
			        		calendar.getTime(),(List<DownloaderCallback>)downloaderCallbackList);
					
					String downloadUri = constructDownloadUri(calendar.getTime());
					
					 /*
		             * Download action
		             */
		            String rawData = null;
		            try{
			            DownloaderUtil.executeCallbacks(barListBreadthDownload,
			            		DownloaderConstant.ACTION.DOWNLOAD,
			            		DownloaderConstant.EVENT.START,
			            		downloadUri, (List<DownloaderCallback>)downloaderCallbackList);
			            
		            	rawData = getRawData(downloadUri);
		            	
			            DownloaderUtil.executeCallbacks(barListBreadthDownload,
			            		DownloaderConstant.ACTION.DOWNLOAD,
			            		DownloaderConstant.EVENT.SUCCESS,
			            		rawData, (List<DownloaderCallback>)downloaderCallbackList);
		            }catch(Exception e){
			            DownloaderUtil.executeCallbacks(barListBreadthDownload,
			            		DownloaderConstant.ACTION.DOWNLOAD,
			            		DownloaderConstant.EVENT.FAIL,
			            		rawData, (List<DownloaderCallback>)downloaderCallbackList);
		            }finally{
			            DownloaderUtil.executeCallbacks(barListBreadthDownload,
			            		DownloaderConstant.ACTION.DOWNLOAD,
			            		DownloaderConstant.EVENT.END,
			            		rawData, (List<DownloaderCallback>)downloaderCallbackList);
		            }
		            
		            if(!StringUtils.hasText(rawData)){
		            	continue;
		            }

		            /*
		             * Parse action
		             */
		            try{
		            	 DownloaderUtil.executeCallbacks(barListBreadthDownload,
				            		DownloaderConstant.ACTION.PARSE,
				            		DownloaderConstant.EVENT.START,
				            		rawData, (List<DownloaderCallback>)downloaderCallbackList);
		            	 
		            	barList = BseBarBredthCSVParser.parseCsv(rawData);
			            for(Bar bar : barList){
			            	bar.setDate(calendar.getTime());
			            }
			            
			            barListBreadthDownload.getBarList().clear();
			            barListBreadthDownload.getBarList().addAll(barList);
			            barListBreadthDownload.setDownloadDate(calendar.getTime());
			            DownloaderUtil.executeCallbacks(barListBreadthDownload,
			            		DownloaderConstant.ACTION.PARSE,
			            		DownloaderConstant.EVENT.SUCCESS,
			            		rawData, (List<DownloaderCallback>)downloaderCallbackList);
			            		
		            }catch(Exception e){
		            	barListBreadthDownload.getBarList().clear();
			            barListBreadthDownload.getBarList().addAll(barList);
			            barListBreadthDownload.setDownloadDate(calendar.getTime());
			            DownloaderUtil.executeCallbacks(barListBreadthDownload,
			            		DownloaderConstant.ACTION.PARSE,
			            		DownloaderConstant.EVENT.FAIL,
			            		e, (List<DownloaderCallback>)downloaderCallbackList);
		            }finally{
			            DownloaderUtil.executeCallbacks(barListBreadthDownload,
			            		DownloaderConstant.ACTION.PARSE,
			            		DownloaderConstant.EVENT.END,
			            		null, (List<DownloaderCallback>)downloaderCallbackList);
		            }
		            		
		            DownloaderUtil.executeCallbacks(barListBreadthDownload,
		            		DownloaderConstant.ACTION.ALL,
		            		DownloaderConstant.EVENT.SUCCESS,
		            		barList, (List<DownloaderCallback>)downloaderCallbackList);
				}catch(Exception e){
					    DownloaderUtil.executeCallbacks(barListBreadthDownload,
			            		DownloaderConstant.ACTION.ALL,
			            		DownloaderConstant.EVENT.FAIL,
			            		e, (List<DownloaderCallback>)downloaderCallbackList);
					if(e.getMessage() != null && (e.getMessage().contains("Server returned HTTP response code"))){
						logger.debug("Server does not have data for date "+calendar.getTime());
					}else{
						logger.error("Error while downloading BSE Bredth data for date : "+calendar.getTime(), e);
					}
				}finally{					
			            DownloaderUtil.executeCallbacks(barListBreadthDownload,
			            		DownloaderConstant.ACTION.ALL,
			            		DownloaderConstant.EVENT.END,
			            		null, downloaderCallbackList);
				}
			}
		}
	}
}
